import os
import json
import time
import logging
import threading
import requests
from datetime import datetime
from flask import current_app
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64

# 导入AES加密模块
try:
    from .utils.AES import AESCipher
except ImportError:
    from ..utils.AES import AESCipher

# 导入配置同步服务
try:
    from .config_sync_service import ConfigSyncService
    from .gitee_config_service import GiteeConfigService
except ImportError:
    # 如果导入失败，定义空的服务类
    class ConfigSyncService:
        def sync_all_configs(self, school_name, school_config):
            return False
    class GiteeConfigService:
        def fetch_school_config(self, school_name):
            return {}, ""

# 日志配置
logging.basicConfig(level=logging.DEBUG, 
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger('device_report')

class DeviceReporting:
    def __init__(self, app=None):
        self.app = app
        self.report_thread = None
        self.report_interval = 86400  # 24小时，但实际只开机时上报一次
        self.is_running = False
        self.last_report_time = None
        self.report_url = None
        self.device_id = None
        self.aes_key = None
        self.aes_cipher = None
        self.startup_reported = False  # 标记是否已完成开机上报
        self.report_status_file = '/tmp/mobox_last_report.txt'  # 记录上次上报时间
        
        if app is not None:
            self.init_app(app)
    
    def init_app(self, app):
        """初始化应用配置"""
        self.app = app
        self.report_interval = app.config.get('DEVICE_REPORT_INTERVAL', 86400)
        self.report_url = app.config.get('DEVICE_REPORT_URL')
        self.aes_key = app.config.get('DEVICE_REPORT_AES_KEY')
        
        # 如果配置了上报URL和AES密钥，检查是否需要上报
        if self.report_url and self.aes_key:
            self.aes_cipher = AESCipher(self.aes_key)
            
            # 检查是否需要开机上报
            if self._should_report_on_startup():
                try:
                    logger.info("系统启动，执行开机设备状态上报")
                    # 根据重构方案，禁用开机时的Gitee配置同步
                    success = self._report_device_status(trigger_sync=False)
                    if success:
                        self._mark_startup_reported()
                        self.startup_reported = True
                        logger.info("开机设备状态上报完成（已禁用Gitee配置同步）")
                    else:
                        logger.warning("开机设备状态上报失败，但不影响系统启动")
                except Exception as e:
                    logger.error(f"开机设备状态上报异常: {str(e)}")
            else:
                logger.info("检测到近期已上报，跳过开机上报")
                self.startup_reported = True
    
    def _should_report_on_startup(self):
        """检查是否应该在启动时上报（距离上次上报超过12小时）"""
        try:
            if os.path.exists(self.report_status_file):
                with open(self.report_status_file, 'r') as f:
                    last_report_str = f.read().strip()
                    if last_report_str:
                        last_report_time = datetime.fromisoformat(last_report_str)
                        # 如果距离上次上报超过12小时，则需要重新上报
                        time_diff = datetime.now() - last_report_time
                        return time_diff.total_seconds() > 43200  # 12小时
            return True  # 没有记录文件或解析失败，需要上报
        except Exception as e:
            logger.warning(f"检查上报状态文件失败: {str(e)}，将执行上报")
            return True
    
    def _mark_startup_reported(self):
        """标记已完成开机上报"""
        try:
            with open(self.report_status_file, 'w') as f:
                f.write(datetime.now().isoformat())
            logger.debug("已更新上报状态文件")
        except Exception as e:
            logger.warning(f"更新上报状态文件失败: {str(e)}")

    def start_reporting(self):
        """保留方法兼容性，但不启动定时上报"""
        logger.info("设备状态上报服务采用开机一次上报模式，无需启动定时服务")
        return

    def stop_reporting(self):
        """保留方法兼容性"""
        self.is_running = False
        logger.info("设备状态上报服务停止（开机上报模式无需停止）")
    
    def _collect_device_status(self):
        """收集设备状态信息 - 按照官方格式"""
        try:
            from .system_monitor import get_system_info, get_box_id, get_system_status, get_mac_address
            
            # 获取设备ID
            if not self.device_id:
                with self.app.app_context():
                    box_id = get_box_id()
                    self.device_id = box_id
            
            # 获取系统信息和状态
            with self.app.app_context():
                system_info = get_system_info()
                system_status = get_system_status()
                mac_address = get_mac_address()
            
            # 按照官方格式组装上报数据
            device_data = {
                "device_id": self.device_id,
                "timestamp": int(time.time()),
                "network": {
                    "ip": system_info.get('ip_address', '192.168.1.*'),
                    "mac": mac_address,
                    "online": True
                },
                "hardware": {
                    "cpu_temp": int(system_status.get('temperature', 0)),
                    "ram_usage": int(system_status.get('memory_percent', 0)),
                    "storage_health": int(100 - system_status.get('disk_percent', 0))
                },
                "software": {
                    "docker_version": "box:v2.1",
                    "os_version": self.app.config.get('DASHBOARD_VERSION', 'v1.0.0'),
                    "last_update": datetime.now().strftime("%Y-%m-%d")
                }
            }
            
            logger.debug(f"收集的设备数据: {device_data}")
            return device_data
        except Exception as e:
            logger.error(f"收集设备状态信息失败: {str(e)}")
            return None
    
    def _encrypt_data(self, raw_str):
        """使用与用户测试脚本完全一致的AES-ECB加密实现"""
        try:
            cipher = AES.new(self.aes_key.encode('utf-8'), AES.MODE_ECB)
            padded = pad(raw_str.encode('utf-8'), AES.block_size)
            encrypted_bytes = cipher.encrypt(padded)
            return base64.b64encode(encrypted_bytes).decode('utf-8')
        except Exception as e:
            logger.error(f"加密失败: {str(e)}")
            return None
    
    def _report_device_status(self, trigger_sync=True):
        """
        上报设备状态信息 - 按照参考文档格式
        
        Args:
            trigger_sync: 是否在平台返回学校信息时触发配置同步，默认为True
        """
        device_data = self._collect_device_status()
        if not device_data:
            return False
        
        try:
            # 将数据转换为JSON字符串
            json_data = json.dumps(device_data)
            logger.info(f"上报数据: {json_data}")
            
            # 使用AES加密数据
            encrypted_data = self._encrypt_data(json_data)
            logger.info(f"加密后数据长度: {len(encrypted_data) if encrypted_data else 0}")
            
            # 按照参考文档格式发送数据
            headers = {
                "Content-Type": "application/x-www-form-urlencoded",
                "tenant-id": "1"
            }
            
            # 数据以表单格式发送
            params = {
                "data": encrypted_data
            }
            
            response = requests.post(
                self.report_url, 
                params=params,  # 使用params而不是data
                headers=headers,
                timeout=10
            )
            
            # 检查响应状态
            logger.info(f"响应状态码: {response.status_code}")
            logger.info(f"响应内容: {response.text}")
            
            if response.status_code == 200:
                try:
                    # 解析响应JSON，检查业务层面的成功标志
                    response_data = response.json()
                    # 只要 code 为 0 就认为上报成功，不检查 data 的具体内容
                    if response_data.get('code') == 0:
                        logger.info(f"设备状态上报成功: {self.device_id}")
                        
                        # 检查是否需要触发配置同步
                        if trigger_sync and response_data.get('data') and isinstance(response_data['data'], dict):
                            school_info = response_data['data'].get('school')
                            if school_info:
                                school_name = school_info.get('name')
                                school_id = school_info.get('id')
                                logger.info(f"设备已绑定学校: {school_name} (ID: {school_id})")
                                logger.info("触发配置同步...")
                                
                                # 触发配置同步
                                self._trigger_config_sync(school_name, school_info)
                        elif not trigger_sync:
                            logger.info("手动上报模式，跳过配置同步")
                            # 仍然记录学校信息，但不触发同步
                            if response_data.get('data') and isinstance(response_data['data'], dict):
                                school_info = response_data['data'].get('school')
                                if school_info:
                                    school_name = school_info.get('name')
                                    school_id = school_info.get('id')
                                    logger.info(f"设备已绑定学校: {school_name} (ID: {school_id}) - 仅记录，不触发同步")
                        
                        return True
                    else:
                        logger.warning(f"设备状态上报业务失败: {response.text}")
                        return False
                except Exception as e:
                    logger.error(f"解析响应失败: {str(e)}, 响应内容: {response.text}")
                    return False
            else:
                logger.warning(f"设备状态上报HTTP失败: {response.status_code}, 响应: {response.text}")
                return False
                
        except requests.RequestException as e:
            logger.error(f"设备状态上报网络错误: {str(e)}")
            return False
        except Exception as e:
            logger.error(f"设备状态上报异常: {str(e)}")
            return False
    
    def report_status(self, trigger_sync=True):
        """
        上报设备状态
        
        Args:
            trigger_sync: 是否在平台返回学校信息时触发配置同步，默认为True
        """
        try:
            result = self._report_device_status(trigger_sync=trigger_sync)
            if result:
                self._mark_startup_reported()  # 更新状态文件
                self.last_report_time = datetime.now()
                if trigger_sync:
                    logger.info("自动设备状态上报完成")
                else:
                    logger.info("手动设备状态上报完成（未触发配置同步）")
            return result
        except Exception as e:
            logger.error(f"上报设备状态失败: {str(e)}")
            return False
    
    def report_status_manual(self):
        """手动上报设备状态 - 不触发配置同步"""
        return self.report_status(trigger_sync=False)
    
    def _trigger_config_sync(self, school_name: str, school_info: dict):
        """
        触发配置同步
        
        Args:
            school_name: 学校名称
            school_info: 学校信息
        """
        try:
            logger.info(f"开始同步学校配置: {school_name}")
            
            # 尝试从Gitee获取学校配置
            try:
                gitee_service = GiteeConfigService()
                school_config, compose_path = gitee_service.fetch_school_config(school_name)
                
                # 使用配置同步服务更新所有相关配置文件
                sync_service = ConfigSyncService()
                success = sync_service.sync_all_configs(school_name, school_config)
                
                if success:
                    logger.info(f"学校配置同步成功: {school_name}")
                else:
                    logger.error(f"学校配置同步失败: {school_name}")
                    
            except Exception as e:
                logger.error(f"从Gitee获取配置失败: {e}")
                # 如果无法从Gitee获取配置，使用默认配置
                self._create_default_config(school_name, school_info)
                
        except Exception as e:
            logger.error(f"配置同步触发失败: {e}")
    
    def _create_default_config(self, school_name: str, school_info: dict):
        """
        根据平台返回的学校信息创建配置
        
        Args:
            school_name: 学校名称  
            school_info: 学校信息
        """
        try:
            # 从平台返回的信息中提取Docker版本
            docker_version = school_info.get('dockerVersion', 'mobox-python-hardware:latest')
            
            # 解析Docker版本信息
            if ':' in docker_version:
                image_name = docker_version.split(':')[0]
                image_tag = docker_version.split(':')[1]
            else:
                image_name = docker_version
                image_tag = 'latest'
            
            # 创建基于平台信息的学校配置
            default_config = {
                "docker": {
                    "registry": {
                        "url": "crpi-axr6nxua9k92sqiz.cn-hangzhou.personal.cr.aliyuncs.com",
                        "public_url": "crpi-axr6nxua9k92sqiz.cn-hangzhou.personal.cr.aliyuncs.com",
                        "username": "mobox",
                        "namespace": "mobox-docker",
                        "use_vpc": True
                    },
                    "services": [
                        {
                            "name": image_name,
                            "image": f"{image_name}:{image_tag}",
                            "remote_image": f"mobox-docker/{image_name}:{image_tag}",
                            "port": 8080,
                            "enabled": True,
                            "description": f"MoBox {image_name.replace('-', ' ').title()} 环境"
                        }
                    ],
                    "auto_backup": True,
                    "backup_retention": 5,
                    "update_strategy": "rolling",
                    "health_check_timeout": 60
                },
                "schedule": {
                    "device_report_interval": 2147483647
                }
            }
            
            logger.info(f"根据平台信息创建配置 - 镜像: {docker_version}")
            
            # 使用配置同步服务更新配置
            sync_service = ConfigSyncService()
            success = sync_service.sync_all_configs(school_name, default_config)
            
            if success:
                logger.info(f"基于平台信息的配置创建成功: {school_name}")
            else:
                logger.error(f"基于平台信息的配置创建失败: {school_name}")
                
        except Exception as e:
            logger.error(f"创建基于平台信息的配置失败: {e}")

    def __del__(self):
        """析构函数确保线程安全退出"""
        try:
            self.stop_reporting()
        except Exception:
            pass

# 全局单例
device_reporter = None

def init_device_reporting(app):
    """初始化设备状态上报服务"""
    global device_reporter
    if device_reporter is None:
        device_reporter = DeviceReporting(app)
    return device_reporter

def get_device_reporter():
    """获取设备状态上报服务实例"""
    global device_reporter
    return device_reporter 